#include <libxml/xmlreader.h>
#include "core/reader/xml_reader.h"

namespace aliyun {

XmlReader::XmlReader() {
}

static void Read(xmlNodePtr xml_node, const string& path_root, bool append_path, std::map<string, string>& map);

bool XmlReader::Parse(const char* buffer, size_t buffer_length, const string& base_key, std::map<string, string>& map) {
  if(NULL == buffer || 0 == buffer_length) {
    return false;
  }
  xmlDocPtr xml_doc = xmlParseMemory(buffer, buffer_length);
  if(NULL == xml_doc) {
    return false;
  }
  xmlNodePtr xml_root = xmlDocGetRootElement(xml_doc);
  if(NULL == xml_root) {
    return false;
  }
  Read(xml_root, base_key, false, map);
  xmlFreeDoc(xml_doc);
  return true;
}

static string BuildPath(xmlNodePtr xml_node, const string& path, bool append_path) {
  return append_path ? path + "." + (const char*)xml_node->name : path;
}

static void ElementsAsList(std::vector<xmlNodePtr> xml_node_list, const string& path, std::map<string, string>& map) {
  int length = (int)xml_node_list.size();
  map[path+".Length"] = string_ValueOfInt(length);
  for(int i = 0; i < length; i++) {
    Read(xml_node_list[i], path+"["+string_ValueOfInt(i)+"]", false, map);
  }
}

static bool GetChildElements(xmlNodePtr xml_node, std::vector<xmlNodePtr>& xml_childs) {
  if (NULL == xml_node) {
    return false;
  }
  for(xmlNodePtr child = xml_node->children; NULL != child; child = child->next){
    if(child->type == XML_ELEMENT_NODE) {
      xml_childs.push_back(child);
    }
  }
  return true;
}
static bool GetChildElements(xmlNodePtr xml_node, std::vector<xmlNodePtr>& xml_childs, const xmlChar* tag_name) {
  if (NULL == xml_node) {
    return false;
  }
  for(xmlNodePtr child = xml_node->children; NULL != child; child = child->next){
    if(child->type == XML_ELEMENT_NODE && xmlStrcmp(child->name, tag_name) == 0) {
      xml_childs.push_back(child);
    }
  }
  return true;
}

static void Read(xmlNodePtr xml_node, const string& path_root, bool append_path, std::map<string, string>& map) {
  if(NULL == xml_node) {
    return ;
  }
  string path = BuildPath(xml_node, path_root, append_path);
  std::vector<xmlNodePtr> xml_childs;
  GetChildElements(xml_node, xml_childs);
  if(xml_childs.size() == 0) {
    const char *content = (const char*)xmlNodeGetContent(xml_node);
    map[path] = content;
    xmlFree((void*)content);
  }
  std::vector<xmlNodePtr> xml_list_childs;
  if(xml_node->children != NULL) {
    GetChildElements(xml_node, xml_list_childs, xml_node->children->name);
  }
  if(xml_list_childs.size() > 1 && xml_childs.size() == xml_list_childs.size()) {//be list
    ElementsAsList(xml_childs, path, map);
  } else if(xml_list_childs.size() == 1 && xml_childs.size() == 1) {//may be list
    ElementsAsList(xml_list_childs, path, map); //as list
    Read(xml_childs[0], path, true, map); //as not list
  } else { //not list
    for(size_t i = 0; i < xml_childs.size(); i++) {
      Read(xml_childs[i], path, true, map);
    }
  }
}

} // namespace aliyun {

// end of file
